import { Block, BlockFun } from './Block';
import { BaseGame, GameStates, GAME_PAD_MATRIX_H, GAME_PAD_MATRIX_W, _LEVEL_MAX, _LEVEL_MIN, _REST_LINE_DURATION, _SPEED } from '../BaseGame';


export class TetrisGame extends BaseGame {
  private _cleared: number = 0;
  private _current: Block = null;
  private _next: Block = BlockFun.getRandom();
  _states: GameStates = GameStates.none;
  private _autoFallTimer;

  constructor() {
    super();
    this.clickConfirm()
  }


  reset() {
    if (this._states === GameStates.none) {
      // 可以开始游戏
      this._startGame();
      return;
    }
    if (this._states === GameStates.reset) {
      return;
    }
    this._states = GameStates.reset;
    (async () => {
      let line = GAME_PAD_MATRIX_H;
      await new Promise<void>(async (resolve) => {
        while (line > 0) {
          line--;
          for (let i = 0; i < GAME_PAD_MATRIX_W; i++) {
            this._data[line][i] = 1;
          }
          this._buildMatrix()
          await new Promise(resolve => setTimeout(resolve, _REST_LINE_DURATION));
        }
        resolve();
      });
      this._current = null;
      this._getNext();
      this._points = 0;
      this._cleared = 0;
      await new Promise<void>(async (resolve) => {
        while (line < GAME_PAD_MATRIX_H) {
          for (let i = 0; i < GAME_PAD_MATRIX_W; i++) {
            this._data[line][i] = 0;
          }
          this._buildMatrix()
          line++;
          await new Promise(resolve => setTimeout(resolve, _REST_LINE_DURATION));
        }
        resolve();
      });
      this._buildMatrix()
      this._states = GameStates.none
    })();
  }

  startOrPause() {
    if (this._states == GameStates.running) {
      this.pause();
    } else if (this._states == GameStates.paused || this._states == GameStates.none) {
      this.start();
    }
  }

  start() {
    this._startGame()
  }

  pause() {
    if (this._states == GameStates.running) {
      this._states = GameStates.paused;
    }
    this._buildMatrix()
  }

  clickD() {
    if (this._states === GameStates.running) {
      const next = this._current?.fall();
      if (next !== null && next.isValidInMatrix(this._data)) {
        this._current = next;
      } else {
        this._mixCurrentIntoData();
      }
      this._buildMatrix()
    }
  }

  clickU() {
    if (this._states === GameStates.running) {
      const next = this._current?.rotate();
      if (next !== null && next.isValidInMatrix(this._data)) {
        this._current = next;
      }
      this._buildMatrix()
    }
  }

  clickR() {
    if (this._states === GameStates.none && this._level < _LEVEL_MAX) {
      this._level++;
    } else if (this._states === GameStates.running) {
      const next = this._current?.right();
      if (next !== null && next.isValidInMatrix(this._data)) {
        this._current = next;
      }
    }
    this._buildMatrix()

  }

  clickL() {
    if (this._states === GameStates.none && this._level > _LEVEL_MIN) {
      this._level--;
    } else if (this._states === GameStates.running) {
      const next = this._current?.left();
      if (next !== null && next.isValidInMatrix(this._data)) {
        this._current = next;
      }
    }
    this._buildMatrix()

  }

  async clickConfirm() {
    if (this._states === GameStates.running) {
      for (let i = 0; i < GAME_PAD_MATRIX_H; i++) {
        const fall = this._current?.fall(i + 1);
        if (fall && !fall.isValidInMatrix(this._data)) {
          this._current = this._current?.fall(i);
          this._states = GameStates.drop;
          this._buildMatrix()
          await new Promise(resolve => setTimeout(resolve, 100));
          this._mixCurrentIntoData();
          break;
        }
      }
      this._buildMatrix()
    } else if (this._states === GameStates.paused || this._states === GameStates.none) {
      this._buildMatrix()
    }
  }


  private async _mixCurrentIntoData(): Promise<void> {
    if (!this._current) {
      return;
    }


    //取消自动下落任务
    this._autoFall(false);

    this._forTable((i, j) => this._data[i][j] = this._current?.get(j, i) ?? this._data[i][j]);

    //消除行
    const clearLines: number[] = [];
    for (let i = 0; i < GAME_PAD_MATRIX_H; i++) {
      if (this._data[i].every((d) => d === 1)) {
        clearLines.push(i);
      }
    }

    if (clearLines.length > 0) {
      this._states = GameStates.clear
      this._buildMatrix()

      //消除效果动画
      for (let count = 0; count < 5; count++) {
        clearLines.forEach((line) => {
          this._mask[line].fill(count % 2 === 0 ? -1 : 1, 0, GAME_PAD_MATRIX_W);
        });
        this._buildMatrix()
        await new Promise(resolve => setTimeout(resolve, 100));
      }
      clearLines.forEach((line) => this._mask[line].fill(0, 0, GAME_PAD_MATRIX_W));

      //移除所有被消除的行
      clearLines.forEach((line) => {
        this._data.copyWithin(1, 0, line + 1);
        this._data[0].fill(0);
      });
      console.log("clear lines: ", clearLines);

      this._cleared += clearLines.length;
      this._points += clearLines.length * this._level * 5;

      //消除行可能提升等级
      const level = Math.floor(this._cleared / 50) + _LEVEL_MIN;
      this._level = level <= _LEVEL_MAX && level > this._level ? level : this._level;
    } else {
      this._states = GameStates.mixing;
      this._forTable((i, j) => this._mask[i][j] = this._current?.get(j, i) ?? this._mask[i][j]);
      this._buildMatrix()
      await new Promise(resolve => setTimeout(resolve, 200));
      this._forTable((i, j) => this._mask[i][j] = 0);
      this._buildMatrix()
    }

    // _current 已经融入 _data 了，所以不再需要
    this._current = null;

    // 检查游戏是否结束，即检查第一行是否有元素为1
    if (this._data[0].includes(1)) {
      this.reset();
      return;
    } else {
      // 游戏尚未结束，开启下一轮方块下落
      this._startGame();
    }
  }

  _forTable(callback: (row: number, column: number) => any): void {
    for (let i = 0; i < GAME_PAD_MATRIX_H; i++) {
      for (let j = 0; j < GAME_PAD_MATRIX_W; j++) {
        const result = callback(i, j);
        if (typeof result === 'boolean' && result) {
          break;
        }
      }
    }
  }

  private _autoFall(enable: boolean): void {
    if (!enable) {
      clearInterval(this._autoFallTimer)
      this._autoFallTimer = null;
    } else if (enable) {
      clearInterval(this._autoFallTimer)
      this._current = this._current || this._getNext();
      this._autoFallTimer = setInterval(() => {
        this.clickD();
      }, _SPEED[this._level - 1]);
    }
  }

  private _startGame(): void {
    if (this._states === GameStates.running && this._autoFallTimer && !this._autoFallTimer.isActive) {
      return;
    }
    this._states = GameStates.running;
    this._autoFall(true);
    this._buildMatrix()
  }

  private _getNext(): Block {
    const next = this._next;
    this._next = BlockFun.getRandom();
    return next;
  }


  private _buildMatrix(): void {
    var mixed: number[][] = [];
    for (let i = 0; i < GAME_PAD_MATRIX_H; i++) {
      mixed.push(Array(GAME_PAD_MATRIX_W).fill(0));
      for (let j = 0; j < GAME_PAD_MATRIX_W; j++) {
        let value = this._current?.get(j, i) ?? this._data[i][j];
        if (this._mask[i][j] === -1) {
          value = 0;
        } else if (this._mask[i][j] === 1) {
          value = 2;
        }
        mixed[i][j] = value;
      }
    }
    this._mixed = mixed
  }
}